const fs = require('fs')
const request = require('request-promise')
const Nightmare = require('nightmare')
const nightmare = Nightmare({ show: true })
const cheerio = require('cheerio')
const mkdirp = require('mkdirp')
const config = require('../config')

const c = console.info

exports.api = async function api(ctx) {
  try {
    !ctx.state.data && (await fetchData(ctx))

    let { title } = ctx.params
    let data = !title
      ? ctx.state.data
      : ctx.state.data.filter(value => {
          return value.title === title
        })

    data = await exportApiFile(data)
    ctx.success({ data })
  } catch (e) {
    ctx.error({ msg: '导出api文件失败' })
  }
}

exports.own = async function own(ctx) {
  try {
    !ctx.state.data && (await fetchData(ctx))

    let { title } = ctx.params
    let data = !title
      ? ctx.state.data
      : ctx.state.data.filter(value => {
          return value.title === title
        })

    data = config.templete && (await exportOwnApiFile(data))
    ctx.success({ data })
  } catch (e) {
    ctx.error({ msg: '导出模版文件失败' })
  }
}

exports.data = async function data(ctx) {
  try {
    !ctx.state.data && (await fetchData(ctx))

    let { title } = ctx.params
    let data = !title
      ? ctx.state.data
      : ctx.state.data.filter(value => {
          return value.title === title
        })

    data = await exportDataFile(data)
    ctx.success({ data })
  } catch (e) {
    ctx.error({ msg: '导出数据文件失败' })
  }
}

// 新版本swagger解析方案 点开所有按钮请求完数据后再分解页面
// fetchData = async ctx => {
//   let body = await nightmare
//     .goto(config.url)
//     .wait(() => document.querySelectorAll('.opblock-tag-section').length)
//     .wait(function() {
//       ;[].forEach.call(
//         document.querySelectorAll('.opblock-tag-section'),
//         function(el) {
//           if (el.querySelector('noscript')) {
//             el.querySelector('button').click()
//           }
//         }
//       )
//       return true
//     })
//     .evaluate(() => document.querySelector('#swagger-ui').innerHTML)

//   ctx.state.data = await parseBody(body)
// }

// parseBody = async body => {
//   let $ = cheerio.load(body)
//   let items = $('.opblock-tag-section')
//   let data = []
//   let obj = {}
//   items.each((i, item) => {
//     let title = $(item)
//       .find('h4>a')
//       .text()
//     items = $(item).find('.opblock')
//     let apis = []
//     let api = {}
//     items.each((i, item) => {
//       let type = $(item)
//         .find('.opblock-summary-method')
//         .text()
//       let path = $(item)
//         .find('.opblock-summary-path>a')
//         .text()
//       let markdown = $(item)
//         .find('.opblock-summary-description')
//         .text()
//       let params = ''
//       let note = `${title}_${markdown}`
//       api = { type, path, markdown, params, note }
//       apis.push(api)
//     })

//     obj = { title, apis }
//     data.push(obj)
//   })
//   return data
// }
//老版本swagger解析方案
fetchData = async ctx => {
  let body = await nightmare
    .goto(config.url)
    .wait('#resources')
    .evaluate(() => document.body.innerHTML)

  ctx.state.data = await parseBody(body)
}

parseBody = async body => {
  let $ = cheerio.load(body)
  let items = $('#resources').children()
  let data = []
  let obj = {}
  items.each((i, item) => {
    let title = $(item)
      .find('h2>a')
      .text()
    items = $(item).find('.endpoint')
    let apis = []
    let api = {}
    items.each((i, item) => {
      let type = $(item)
        .find('.http_method>a')
        .text()
      let path = $(item)
        .find('.path>a')
        .text()
      let markdown = $(item)
        .find('.options .markdown>p')
        .text()
      let params = $(item)
        .find('.operation-params .hljs-attr')
        .map((i, item) => $(item).text())
        .get()
        .join(',')
      let note = `${title}_${markdown}`
      api = { type, path, markdown, params, note }
      apis.push(api)
    })

    obj = { title, apis }
    data.push(obj)
  })
  return data
}

writeFile = async (fileName, data) => {
  return new Promise((resolve, reject) => {
    return fs.writeFile(fileName, data, e => {
      if (e) {
        reject(e)
      }
      resolve(data)
    })
  }).catch(e => {
    throw e
  })
}

readFile = async fileName => {
  return new Promise((resolve, reject) => {
    return fs.readFile(fileName, 'utf-8', (e, data) => {
      if (e) {
        reject(e)
      }
      resolve(data)
    })
  }).catch(e => {
    throw e
  })
}

const compile = template => keys => eval(`({${keys}})=>${template}`)

parse = ({ type, path, note }) => {
  if (!(type && path && note)) return ''
  return (
    //会影响导出时的表现
    `//${note}
API_${type.toUpperCase()}${path
      .replace(/[/-]/g, '_')
      .replace(/[{}]/g, '')
      .toUpperCase()}: {
url: '${path.substring(1)}',
    config: {
        method: '${type}',
        host: '${config.host}'
    }
},
`
  )
}
//旧版本拼装方法
// parse = ({ type, path, note }) => {
//   if (!(type && path && note)) return ''
//   return (
//     //会影响导出时的表现
//     `//${note}
// API_${type.toUpperCase()}_${path
//       .match(/[^\/\{\}]+(?=($|(.\{.+\})+$))/)[0]
//       .toUpperCase()}: {
// url: '${path.substring(1)}',
//     config: {
//         method: '${type}',
//         host: 'normal'
//     }
// },
// `
//   )
// }

exportApiFile = async data => {
  try {
    let api = ''
    data.forEach(item => {
      api += item.apis.reduce((tol, cur) => {
        return (tol = `${tol}${parse(cur)}`)
      }, '')
    })

    mkdirp(config.exportDir)
    return await writeFile(config.defaultPath, `${api}`)
  } catch (e) {
    throw e
  }
}

exportOwnApiFile = async data => {
  try {
    let api = ''
    let parse = compile(config.templete)
    data.forEach(item => {
      api += item.apis.reduce((tol, cur) => {
        return (tol = `${tol}${parse(Object.keys(cur))(cur)}`)
      }, '')
    })

    mkdirp(config.exportDir)
    return await writeFile(config.ownPath, `${api}`)
  } catch (e) {
    throw e
  }
}

exportDataFile = async data => {
  try {
    mkdirp(config.exportDir)
    return await writeFile(config.dataPath, JSON.stringify(data))
  } catch (e) {
    throw e
  }
}
